package com.pisolution.scheduler.runtime.quartz;

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.jdbcjobstore.JobStoreTX;
import org.quartz.impl.jdbcjobstore.StdJDBCDelegate;
import org.quartz.simpl.SimpleThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Properties;

/**
 * 定时调度服务.
 *
 * @author sora
 * @version 0.1.0
 */
@Service
public class SchedulerQuartzService {
    private static final Logger LOGGER = LoggerFactory.getLogger(SchedulerQuartzService.class);

    // -------------------------------------------------------------------------------------------------

    /** 用于配置定时调度服务. */
    private final QuartzConfig config;

    /** 用于管理定时调度任务. */
    private Scheduler scheduler;

    // -------------------------------------------------------------------------------------------------
    //  初始化
    // -------------------------------------------------------------------------------------------------

    public SchedulerQuartzService(final QuartzConfig config) {
        this.config = config;
    }

    // -------------------------------------------------------------------------------------------------
    //  生命周期
    // -------------------------------------------------------------------------------------------------

    /**
     * 等待调度服务配置读取完成后初始化调度服务.
     *
     * <p><b>注意:</b></p>
     * <p>该方法会在构造函数与依赖注入之后自动调用 无需手动触发.</p>
     */
    @SuppressWarnings("unused")
    @PostConstruct
    public void configure() {
        try {
            // 1. 创建配置文件读取器以初始化调度服务.
            final StdSchedulerFactory factory = new StdSchedulerFactory();
            final Properties properties = new Properties();

            // 2. 载入配置 (详情配置项参考 http://www.quartz-scheduler.org/documentation/quartz-2.3.0/configuration).
            properties.setProperty("org.quartz.scheduler.instanceName", this.config.getInstanceName());
            properties.setProperty("org.quartz.scheduler.instanceId", this.config.getInstanceId());
            properties.setProperty("org.quartz.scheduler.threadName", this.config.getThreadName());
            properties.setProperty("org.quartz.scheduler.makeSchedulerThreadDaemon", this.config.getDaemon());
            properties.setProperty("org.quartz.scheduler.skipUpdateCheck", "true");

            properties.setProperty("org.quartz.threadPool.class", SimpleThreadPool.class.getName());
            properties.setProperty("org.quartz.threadPool.threadCount", this.config.getThreadPoolSize());
            properties.setProperty("org.quartz.threadPool.threadPriority", this.config.getThreadPoolPriority());
            properties.setProperty("org.quartz.threadPool.makeThreadsDaemons", this.config.getIsThreadPoolDaemon());

            properties.setProperty("org.quartz.jobStore.class", JobStoreTX.class.getName());
            properties.setProperty("org.quartz.jobStore.driverDelegateClass", StdJDBCDelegate.class.getName());
            properties.setProperty("org.quartz.jobStore.dataSource", this.config.getDatasourceName());
            properties.setProperty("org.quartz.jobStore.tablePrefix", this.config.getTablePrefix());
            properties.setProperty("org.quartz.jobStore.useProperties", "false");
            properties.setProperty("org.quartz.jobStore.misfireThreshold", this.config.getMisFireThreshold());
            properties.setProperty("org.quartz.jobStore.isClustered", "true");
            properties.setProperty("org.quartz.jobStore.clusterCheckinInterval", this.config.getCheckinInterval());
            properties.setProperty("org.quartz.jobStore.acquireTriggersWithinLock", "true");
            properties.setProperty(
                String.format("org.quartz.dataSource.%s.connectionProvider.class", this.config.getDatasourceName()),
                DruidConnectionProvider.class.getName()
            );

            // 3. 生成调度服务.
            factory.initialize(properties);

            this.scheduler = factory.getScheduler();
        } catch (SchedulerException e) {
            LOGGER.error(e.getMessage(), e);

            System.exit(1);
        }
    }

    /**
     * 如果调度服务尚未启动 则启动调度服务.
     *
     * @throws SchedulerException 如果无法启动或确认调度服务的状态.
     */
    public void start() throws SchedulerException {
        if (!this.scheduler.isStarted()) {
            this.scheduler.start();

            LOGGER.info("The quartz service started.");
        }
    }

    /**
     * 如果调度服务尚未关闭 则关闭调度服务.
     *
     * @throws SchedulerException 如果无法关闭或确认调度服务的状态.
     */
    public void stop() throws SchedulerException {
        if (!this.scheduler.isShutdown()) {
            this.scheduler.shutdown();

            LOGGER.info("The quartz service stopped.");
        }
    }
}
